<template>
  <div
    ref="imgContainer"
    class="img-container"
    :class="{ preview: isPreviewing }"
    @mousedown="handleMousedown"
    @touchstart="handleMousedown"
  >
    <div v-if="!isPreviewing && list.length > 1" class="count-info">
      {{ list.length }} 张
    </div>
    <!-- <transition :name="'slide-' + slideType"> -->
    <ul v-if="isPreviewing" class="img-list" ref="imgList">
      <li v-for="(item, index) in list">
        <img
          class="img"
          :class="{ active: active === index }"
          :src="$withBase((baseUrl || '') + item)"
          draggable="false"
        />
      </li>
    </ul>
    <img
      v-else
      class="cover-img"
      :src="$withBase((baseUrl || '') + list[0])"
      draggable="false"
      @click="isPreviewing = true"
    />
  </div>
</template>

<script>
import _ from "lodash";
export default {
  name: "ImgPreview",
  props: {
    baseUrl: {
      type: String,
      default: "",
    },
    list: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      isMobile: false, // 是否是否移动端
      touch: {},

      active: 0,
      isPreviewing: false, // 是否在预览

      isMousemoving: "", // 是否正在滑动，next：从右往左滑；pre：从左往右滑； single:单个，multiple：多点触控  G
      touchstarts: [], // 鼠标按下的点集合，移动端多点
      transformImgs: [], // 缩放/拖拽后 的图片集合
    };
  },
  mounted() {
    const isMobile = this.handleIsMobile();
    this.isMobile = isMobile;
    this.touch = {
      down: isMobile ? "touchstart" : "mousedown",
      move: isMobile ? "touchmove" : "mousemove",
      up: isMobile ? "touchend" : "mouseup",
    };
  },
  methods: {
    // 重置预览功能
    resetPreview() {
      this.resetImgTransform(); // 重置图片缩放/拖拽

      this.active = 0;
      this.isPreviewing = false; // 是否在预览
      this.isMousemoving = ""; // 是否正在滑动，next：从右往左滑；pre：从左往右滑； single:单个，multiple：多点触控  G
      this.touchstarts = []; // 鼠标按下的点集合，移动端多点
      this.transformImgs = []; // 缩放/拖拽后 的图片集合
    },

    // 重置图片缩放/拖拽
    resetImgTransform() {
      this.transformImgs.forEach((node) => {
        node.scale = 1;
        node.style.transform = "";
      });
    },

    // 鼠标按下
    handleMousedown(e) {
      // 是否正在预览
      if (this.isPreviewing) {
        this.touchstarts = this.isMobile ? e.touches : [e];
        this.handleTouchMove("open");
        this.handleTouchUp("open");
      }
    },
    // 鼠标移动
    handleMousemove(e) {
      e.preventDefault();
      this.handleImgScale(e); // 操作图片缩放
      this.handleImgMove(e); // 操作图片平移
    },
    // 鼠标抬起
    handleMouseup(e) {
      if (!this.isPreviewing) return false;

      // 此时如果存在滑动状态，必定是缩放，跳过下面判断
      if (!this.isMousemoving || this.isMousemoving === "translate") {
        const isChange = this.handleImgChange(e); // 综合判断并触发图片切换
        if (!isChange && !this.isMousemoving) this.resetPreview(); // 如果没有触发切换，即单纯的点击，初始化预览
      }
      this.touchstarts = [];
      this.isMousemoving = ""; // 重置触摸状态
      this.handleTouchMove("close"); // 关闭move
      this.handleTouchUp("close"); // 关闭up
    },

    // 应用变换
    handleApplayTransform(type, img, newVal = []) {
      const oldTransforms = img.style.transform.split(") ").map((str) => {
        if (str && str.indexOf(")") === -1) {
          str += ")"; // 如果末尾括被截取手动拼接上去
        }

        // 将平移的属性增加上去
        if (type === "translate" && str.indexOf("translate") !== -1) {
          // newVal: [translateX,translateY]
          const reg = /translate\((.*)\)/;
          const translateStr = str.match(reg)[1]; // 提取旧值 "1px, 2px"
          if (translateStr) {
            const strs = translateStr.split(","); // ['1px','2px']

            const realX = newVal[0] + parseInt(strs[0], 10);
            const realY = newVal[1] + parseInt(strs[1], 10);

            str = str.replace(reg, `translate(${realX}px, ${realY}px)`);
          }
        }

        // 将缩放的属性增加上去
        if (type === "scale" && str.indexOf("scale") !== -1) {
          // newVal: [scale]
          str = str.replace(/scale\((.*)\)/, `scale(${newVal[0]})`); // 直接替换
        }

        return str;
      });
      img.style.transform =
        oldTransforms.join(" ") || `scale(1) translate(0px, 0px)`;
    },

    // 操作图片切换， 100ms后，还不存在 isMousemoving 即：没有触发缩放
    handleImgChange(e) {
      const intensity = this.isMobile ? 100 : 380; // 左滑右滑的力度，超过这个将切换图片
      const startX = this.touchstarts[0].clientX; // 开始触摸位置
      const endX = this.isMobile ? e.changedTouches[0].clientX : e.clientX; // 弹起位置
      const moveDistance = startX - endX; // >0，说明从右往左滑，切换下一张。<0，说明从左往右滑，切换上一张
      const clientWidth = this.getVWValue();

      const isRealMove = Math.abs(moveDistance) > intensity; // 判断是否是有效滑动（必须大于指定力度）
      if (isRealMove) {
        if (moveDistance > 0 && this.active < this.list.length - 1) {
          this.active += 1;
          console.log(this.active);
          this.$refs.imgList.style.left = `-${this.active * clientWidth}px`;
          this.resetImgTransform();
          this.isMousemoving = "next";
        } else if (moveDistance < 0 && this.active > 0) {
          this.active -= 1;
          this.$refs.imgList.style.left = `-${this.active * clientWidth}px`;
          this.resetImgTransform();
          this.isMousemoving = "prev";
        }
      }
      return Math.abs(moveDistance) > 10; // 不需要那么精确，只要移动距离不大于10就算单击
    },
    // 操作图片移动
    handleImgMove(e) {
      const startX = this.touchstarts[0].clientX; // 开始触摸位置
      const startY = this.touchstarts[0].clientY; // 开始触摸位置
      const endX = this.isMobile ? e.touches[0].clientX : e.x; // 当前触摸位置
      const endY = this.isMobile ? e.touches[0].clientY : e.y; // 当前触摸位置
      const intensity = this.isMobile ? 15 : 6; // 平移的力度

      const img = this.$refs.imgContainer.querySelector(".img.active");

      const translateX = (endX - startX) / intensity;
      const translateY = (endY - startY) / intensity;

      this.handleApplayTransform("translate", img, [translateX, translateY]); // 应用变换

      !this.transformImgs.some((n) => n === img) &&
        this.transformImgs.push(img); // 如果没存当前操作的图片，则保存一下
      this.isMousemoving = "translate";
    },
    // 操作图片缩放
    handleImgScale(e) {
      if (this.isMobile && e.touches.length > 1) {
        this.isMousemoving = "scale";

        // 多点触控，控制缩放
        const img = document.querySelector(".img-container .img.active");
        const scaleOption = {
          max: 4,
          min: 0.8,
          step: 0.05, // 缩放步长，值越大，缩放越快
        };

        // 获取坐标之间的距离
        var getDistance = function(start, stop) {
          return Math.hypot(stop.x - start.x, stop.y - start.y);
        };

        // 双指缩放距离
        const distance = getDistance(
          {
            x: e.touches[0].pageX,
            y: e.touches[0].pageY,
          },
          {
            x: e.touches[1].pageX,
            y: e.touches[1].pageY,
          }
        );

        // 触碰时距离
        const oldDistance = getDistance(
          {
            x: (this.touchstarts[0] || {}).pageX || 0,
            y: (this.touchstarts[0] || {}).pageY || 0,
          },
          {
            x: (this.touchstarts[1] || {}).pageX || 0,
            y: (this.touchstarts[1] || {}).pageY || 0,
          }
        );
        const distanceDiff = distance - oldDistance; // 新距离-按下的距离，即可知道是放大还是缩小

        const oldImgNode = this.transformImgs.find((n) => n === img);

        let scale = (oldImgNode || {}).scale || 1;

        // distanceDiff大于0放大，小于0缩小
        if (distanceDiff > 0 && scale < scaleOption.max) {
          scale += scaleOption.step;
        } else if (scale > scaleOption.min) {
          scale -= scaleOption.step;
        }

        // 这里有时会获取不到图片节点
        if (img) {
          this.handleApplayTransform("scale", img, [scale]); // 应用变换
          img.scale = scale; // 保存scale
          !oldImgNode && this.transformImgs.push(img); // 如果没存当前操作的图片，则保存一下
        }
      }
    },

    // 开启/关闭 move 的监听
    handleTouchMove(type) {
      if (type === "open") {
        document.addEventListener(this.touch.move, this.handleMousemove);
      } else {
        document.removeEventListener(this.touch.move, this.handleMousemove);
      }
    },
    // 开启/关闭 up 的监听
    handleTouchUp(type) {
      if (type === "open") {
        document.addEventListener(this.touch.up, this.handleMouseup);
      } else {
        document.removeEventListener(this.touch.up, this.handleMouseup);
      }
    },
    // 判断是否是移动端
    handleIsMobile() {
      let info = (navigator || {}).userAgent || "";
      let agents = [
        "Android",
        "iPhone",
        "SymbianOS",
        "Windows Phone",
        "iPod",
        "iPad",
      ];
      for (let i = 0; i < agents.length; i++) {
        if (info.indexOf(agents[i]) >= 0) return true;
      }
      return false;
    },
    // 获取设备视口宽度
    getVWValue() {
      let html = document.documentElement;
      return (
        self.innerWidth ||
        (html && html.offsetWidth) ||
        document.body.offsetWidth
      );
    },
  },
};
</script>

<style lang="scss" scoped>
.count-info {
  position: absolute;
  top: 0;
  color: #fff;
  font-size: 20px;
  background: rgba(0, 0, 0, 0.6);
  width: 100%;
  height: 34px;
  line-height: 34px;
  text-align: right;
  padding-right: 15px;
  box-sizing: border-box;
}

.img-list {
  transition: all 0.2s linear;
  position: absolute;
  left: 0;
  display: flex;
  li {
    width: 100vw;
    display: flex;
    justify-content: center;
    align-items: center;
    img {
      // height: 94vh;
    }
  }
}

@media all and (min-width: 320px) and (max-width: 1360px) {
  .img-container {
    position: relative;
    width: 100%;
    .img-list {
      position: relative;
    }
  }
}

// @media screen and (min-width: 320px) and (max-width: 750px) {
//   .img-container {
//     position: relative;
//     width: 100%;
//     .img-list {
//       position: relative;
//     }
//   }
// }

.img-container.preview {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: #fff;
  z-index: 20210310;
  display: flex;
  align-items: center;
}
</style>
